﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using UnityEngine;
using UnityEngine.UI;

public enum MessageType
{
    beat,
    message
}
public class Client : MonoBehaviour
{
    [SerializeField]
    GameObject m_ReclectButton;
    [SerializeField]
    bool m_IsEditoerMode;
    string ip
    {
        get
        {
            return m_IsEditoerMode ? "127.0.0.1" : "139.186.64.240";
        }
    }
    [SerializeField]
    InputField m_Inputer;
    [SerializeField]
    Text m_RecieveInput;
    public uint maxShowMessageNum = 4;
    LinkedList<string> m_GetInput;
    Socket m_SocketCommunication;
    Thread m_SocketThread;
    int m_Total = 0;
    string m_Information;
    int beatTime;
    int BytsNumPerTemp
    {
        get
        {
            return 1024 * 1024 * 2;
        }
    }
    Byte[] m_TempBytes;
    bool m_BreakLink = true;

    int beatTimeSpace = 13;
    float m_BeatTimeStamp = 0;

    bool m_FreshBeatTimeStampDirty;

    // Use this for initialization
    void Start()
    {
        m_TempBytes = new Byte[BytsNumPerTemp];
        m_GetInput = new LinkedList<string>();
        StartSocketLink();
    }

    private void Update()
    {
        if (newInfoDirty)
        {
            newInfoDirty = false;
            m_RecieveInput.text = "";
            foreach (string str in m_GetInput)
            {
                m_RecieveInput.text += str + "\n";
            }
        }
        if (m_BreakLink && !m_ReclectButton.gameObject.activeSelf)
            m_ReclectButton.gameObject.SetActive(true);
        else if (!m_BreakLink && m_ReclectButton.gameObject.activeSelf)
            m_ReclectButton.gameObject.SetActive(false);
        if(m_FreshBeatTimeStampDirty)
        {
            m_BeatTimeStamp = Time.time;
            m_FreshBeatTimeStampDirty = false;
        }

        if (!m_BreakLink)
        {
            if( Time.time - m_BeatTimeStamp > beatTimeSpace)
            {
                SocketBreak(true);
            }
        }
        //if(m_Information!=null)
        //{
        //    m_RecieveInput.text = m_Information;
        //    lock(m_Information)
        //    {
        //        m_Information = null;
        //    }
        //}
    }

    void StartCollect()
    {
        string linkIP = ip;
        //#if UNITY_EDITOR
        //        linkIP = "127.0.0.1";

        //        //#else
        //        //ip = "139.186.64.240";
        //        //ip = "0.0.0.0";
        //#endif
        //        //ip = "139.186.64.240";

        m_Information = linkIP;
        IPAddress adress = IPAddress.Parse(linkIP);
        //IPAddress adress = IPAddress.Any;
        IPEndPoint port = new IPEndPoint(adress, 51000);
        m_SocketCommunication = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        Debug.Log("LinkStart");
        try
        {
            m_SocketCommunication.Connect(port);
        }
        catch
        {
            SocketBreak(true);
            return;
        }
        Debug.Log("LinkComplete");
        m_FreshBeatTimeStampDirty = true;
        byte[] tempBuffer = new byte[1024 * 1024 * 2];
        byte[] lengthNumBytes = new byte[4];
        List<Byte> recieveBuffer = new List<byte>();
        int recievedBytes = 0;
        float recieveBufferLength = 0;
        Int32 recieveDataNum = 0;
        int recieveNum = 0;
        while (true)
        {
            try
            {
                recieveNum = m_SocketCommunication.Receive(tempBuffer, SocketFlags.None);
            }
            catch
            {
                SocketBreak(true);
                return;
            }
            if(recieveNum == 0)
            {
                SocketBreak(true);
                return;
            }
            recievedBytes += recieveNum;
            for (int idx = 0; idx < recieveNum; ++idx)
            {
                recieveBuffer.Add(tempBuffer[idx]);
            }
            if (recieveDataNum <= 0)
            {
                if (recieveBuffer.Count < 4)
                    continue;
                recieveDataNum = (Int32)recieveBuffer[0] + ((Int32)(recieveBuffer[1]) << 8) + ((Int32)(recieveBuffer[2]) << 16) + ((Int32)(recieveBuffer[3]) << 32);
            }
            if (recieveDataNum <= recieveBuffer.Count)
            {
                recieveDataNum = 0;
                TakeCareRecievedData(recieveBuffer.ToArray());
                recieveBuffer.Clear();
            }
        }
        return;
    }
    bool newInfoDirty;
    void AddRecieveInfo(string getInfo)
    {
        newInfoDirty = true;
        m_GetInput.AddFirst(getInfo);
        while (m_GetInput.Count > maxShowMessageNum)
        {
            m_GetInput.RemoveLast();
        }
    }

    public void Send()
    {
        if (m_Inputer.text != null || m_Inputer.text != "")
        {
            MessageInfo message = PackageMessage(m_Inputer.text);
            m_SocketCommunication.Send(message.bytes, 0, message.size, SocketFlags.None);
        }
        m_Inputer.text = "";
    }

    MessageInfo PackageMessage(string message)
    {
        MessageInfo newMessage = new MessageInfo();
        if (message == "" || message == null)
            return newMessage;
        Int32 length = Encoding.UTF8.GetBytes(message, 0, message.Length, m_TempBytes, 5) + 5;
        Int32 headByteValue = length;
        for (int idx = 0; idx < 4; ++idx)
        {
            m_TempBytes[idx] = (byte)headByteValue;
            headByteValue = headByteValue >> 8;
        }
        m_TempBytes[4] = (Byte)MessageType.message;
        newMessage.bytes = m_TempBytes;
        newMessage.size = length;
        return newMessage;
    }
    void TakeCareRecievedData(Byte[] byts)
    {
        MessageType type = (MessageType)byts[4];
        switch (type)
        {
            case MessageType.beat:
                m_SocketCommunication.Send(byts);
                m_FreshBeatTimeStampDirty = true;
                break;
            case MessageType.message:
                AddRecieveInfo(Encoding.UTF8.GetString(byts, 5, byts.Length - 5));
                break;
        }
    }

    void SocketBreak(bool shutDown = false)
    {
        try
        {
            if (shutDown)
            {
                m_SocketCommunication.Shutdown(SocketShutdown.Both);
                m_SocketCommunication.Close();
            }
        }
        catch
        {

        }
        m_SocketCommunication = null;
        m_BreakLink = true;

    }
    public void StartSocketLink()
    {
        m_BreakLink = false;
        m_ReclectButton.gameObject.SetActive(false);
        m_SocketThread = new Thread(StartCollect);
        m_SocketThread.Start();
    }
}

struct MessageInfo
{
    public Byte[] bytes;
    public int size;
}

